Lets us read the file.

library(readr)
lyrics <- read_csv("songdata.csv")
Parsed with column specification:
cols(
  artist = col_character(),
  song = col_character(),
  link = col_character(),
  text = col_character()
)
head(lyrics)

Lets us examine the dimension of the lyrics dataframe.

dim(lyrics)
[1] 57650     4
library(dplyr)
glimpse(lyrics)
Observations: 57,650
Variables: 4
$ artist <chr> "ABBA", "ABBA", "ABBA", "ABBA", "ABBA", "ABBA", "ABBA", "ABBA", "ABBA", "ABBA", "ABBA", "ABBA", "A...
$ song   <chr> "Ahe's My Kind Of Girl", "Andante, Andante", "As Good As New", "Bang", "Bang-A-Boomerang", "Burnin...
$ link   <chr> "/a/abba/ahes+my+kind+of+girl_20598417.html", "/a/abba/andante+andante_20002708.html", "/a/abba/as...
$ text   <chr> "Look at her face, it's a wonderful face  \nAnd it means something special to me  \nLook at the wa...

Analysis of 55000+ lyrics data - Number of artists - Which artist has highest and lowest number of songs - Distribution of songs of all artists in the dataset - Distribution of lyrics length - Which song lyrics has maximum number of words - Which song lyrics has minimum number of words - Distribution of words count in title - Which songs title has maximum number of words - Which songs title has minimum number of words - WordClouds of titles with minimum and maximum lengths - Is there a relation between title length and song length?

Let’s start with finding out how many artists are listed in the data. Also, how many songs each artist has.

artist<- as.data.frame(table(as.data.frame(lyrics$artist)))
colnames(artist) <- c("artist", "Num_of_songs")
head(artist)

Let’s see the which artist has most and least number of songs in the dataset.

most_songs <- arrange(artist, desc(Num_of_songs))
most_songs

least_songs <- tail(most_songs, 15)
p2 <- ggplot(data = least_songs, aes(artist, Num_of_songs, fill = Num_of_songs)) +
      geom_bar(stat = "identity") +
      geom_text(aes(label=Num_of_songs), vjust=1.6, color="white", size=3) +
      ggtitle("Artists with least number of songs") +
      tilt_theme
p2

Let’s check the distribution of songs for all artists.

p3 <- ggplot(artist, aes(x=Num_of_songs)) + 
 geom_histogram(aes(y=..density..), colour="black", fill="white")+
 geom_density(alpha=.2, fill="red")
p3

Let’s analyze the number of words in each song and its distribution.

library(stringr)
count_words <- function(vec){
  return (length(unlist((str_extract_all(tolower(vec), '\\w+')))))
}
lyrics$word_count <- sapply(lyrics$text, count_words)
head(lyrics$word_count)
[1] 161 272 322 257 255 115
p4 <- ggplot(lyrics, aes(x=word_count)) + 
 geom_histogram(aes(y=..density..), colour="black", fill="white")+
 geom_density(alpha=.2, fill="red")
p4

Let’s analyze the title of the songs, their wordcount and their distribution

lyrics$title_word_count <- sapply(lyrics$song, count_words)
head(lyrics$title_word_count)
[1] 6 2 4 1 3 3

Let’s check out the songs that are longest and shortest.

longest_song <- arrange(lyrics, desc(word_count))
longest_song <- head(longest_song, 10)
shortest_song <- arrange(lyrics, word_count)
shortest_song <- head(shortest_song, 10)
longest_song
shortest_song
p5 <- ggplot(data = longest_song, aes(song, word_count, fill = title_word_count)) +
      geom_bar(stat = "identity") +
      geom_text(aes(label=title_word_count), vjust=1.6, color="white", size=3) +
      ggtitle("Longest Songs") +
      tilt_theme
p6 <- ggplot(data = shortest_song, aes(song, word_count, fill = title_word_count)) +
      geom_bar(position = "dodge", stat = "identity") +
      geom_text(aes(label = title_word_count), vjust = 1.6, color = "white", size = 3) +
      ggtitle("Shortest Songs") +
      tilt_theme
multiplot(p5, p6, cols=2)

p7 <- ggplot(lyrics, aes(x=title_word_count)) + 
 geom_histogram(aes(y=..density..), colour="black", fill="white", binwidth = 1, bins = 1)+
 geom_density(alpha=.2, fill="red")
p7

WordCloud of popular words from song titles

library(wordcloud)
library(SnowballC)
library(RColorBrewer)
library(tm)
texts <- lyrics$song
#texts <- iconv(texts, to = "utf-8")
corpus <- Corpus(VectorSource(texts))
corpus <- tm_map(corpus, PlainTextDocument)
corpus <- tm_map(corpus, removePunctuation)
corpus <- tm_map(corpus, removeWords, stopwords('english'))
corpus <- tm_map(corpus, stemDocument)
corpus <- tm_map(corpus, removeWords, c("and", "this", "there")) 
corpus <- Corpus(VectorSource(corpus))
dtm <- TermDocumentMatrix(corpus)
m <- as.matrix(dtm)
v <- sort(rowSums(m),decreasing=TRUE)
d <- data.frame(word = names(v),freq=v)
head(d, 10)
d <- d[-which(d$word %in% c("and","this","that")),]
set.seed(1234)
wordcloud(words = d$word, freq = d$freq, min.freq = 1,
          max.words=200, random.order=FALSE, rot.per=0.35, 
          colors=brewer.pal(8, "Dark2"))

There are many song titles that are of length 1, 2 and 3. But surprisingly, there are titles of length more than 13 too. Let’s check them out.

longest_title <- subset(lyrics, lyrics$title_word_count > 13)
longest_title
shortest_title <- subset(lyrics, lyrics$title_word_count == 1)
shortest_title

There are 8 songs with title length more than 13 and 8342 songs with single word title. Let’s see word cloud of single word titles and longest titles

texts <- longest_title$song
corpus <- Corpus(VectorSource(texts))
corpus <- tm_map(corpus, PlainTextDocument)
corpus <- Corpus(VectorSource(corpus))
dtm <- TermDocumentMatrix(corpus)
m <- as.matrix(dtm)
v <- sort(rowSums(m),decreasing=TRUE)
d <- data.frame(word = names(v),freq=v)
head(d, 10)
set.seed(1234)
wordcloud(words = d$word, freq = d$freq, min.freq = 1,scale=c(2,0.5),
          max.words=100, random.order=FALSE, rot.per=0.35, 
          colors=brewer.pal(8, "Dark2"))

texts <- shortest_title$song
corpus <- Corpus(VectorSource(texts))
corpus <- tm_map(corpus, PlainTextDocument)
corpus <- Corpus(VectorSource(corpus))
dtm <- TermDocumentMatrix(corpus)
m <- as.matrix(dtm)
v <- sort(rowSums(m),decreasing=TRUE)
d <- data.frame(word = names(v),freq=v)
head(d, 10)
set.seed(1234)
wordcloud(words = d$word, freq = d$freq, min.freq = 1,scale=c(2,0.5),
          max.words=100, random.order=FALSE, rot.per=0.35, 
          colors=brewer.pal(8, "Dark2"))

An interesting questin would be is there relation between length of title and songs? Most probably now, but let’s check out.

p8 <- ggplot(lyrics, aes(x=factor(title_word_count), y=word_count, fill = factor(title_word_count))) + 
  geom_boxplot() 
p8 

cor(lyrics$title_word_count, lyrics$word_count)
[1] -0.02509779

As expected, there is no correlation between these two quantitites.

Let us fix the contracted words to their full forms first.

# function to expand contractions in an English-language source
fix.contractions <- function(doc) {
  # "won't" is a special case as it does not expand to "wo not"
  doc <- gsub("won't", "will not", doc)
  doc <- gsub("can't", "can not", doc)
  doc <- gsub("n't", " not", doc)
  doc <- gsub("'ll", " will", doc)
  doc <- gsub("'re", " are", doc)
  doc <- gsub("'ve", " have", doc)
  doc <- gsub("'m", " am", doc)
  doc <- gsub("'d", " would", doc)
  # 's could be 'is' or could be possessive: it has no expansion
  doc <- gsub("'s", "", doc)
  return(doc)
}
# fix (expand) contractions
lyrics$text <- sapply(lyrics$text, fix.contractions)

Remove special characters from lyrics

# function to remove special characters
removeSpecialChars <- function(x) gsub("[^a-zA-Z0-9 ]", " ", x)
# remove special characters
lyrics$text <- sapply(lyrics$text, removeSpecialChars)

Convert all lyrics text to lower case

# convert everything to lower case
lyrics$text <- sapply(lyrics$text, tolower)

Let’s check the structure of one lyrics to see the changes.

str(lyrics[13, ]$text, nchar.max = 300)
 chr "changing  moving in a circle   i can see your face in all of my dreams   smiling  laughing from the shadows   when i hear your voice  i know what it means   i know it does not matter just how hard i try   you are all the reason for my life      disillusion  disillusion all you left "| __truncated__

SENTIMENT ANALYSIS OF LYRICS Let us perform sentiment analysis on the lyrics. There are various types of sentiment lexicons that can be used. Lets us have a look on them.

library(tidytext)
library(tidyr)
get_sentiments("afinn")
get_sentiments("bing")
get_sentiments("nrc")
get_sentiments("loughran")

nrc seems to have large number of words and their sentiments compared to other two.

nrc_sentiment <- get_sentiments("nrc")
unique(nrc_sentiment$sentiment)
 [1] "trust"        "fear"         "negative"     "sadness"      "anger"        "surprise"     "positive"    
 [8] "disgust"      "joy"          "anticipation"

Let us find the sentiments of each lyrics based on each NRC sentiments.

lyrics_words <- select(lyrics, c("artist", "text"))
lyrics_words <- lyrics_words %>% unnest_tokens(word, text)
head(lyrics_words)

Let’s see words that depict “joy”

joy <- lyrics_words %>%
  inner_join(get_sentiments("nrc") %>% 
  filter(sentiment == "joy")) 
Joining, by = "word"
joy <- as.data.frame(sort(table(joy$word)))
columns_sentiment <- c("word", "Freq")
colnames(joy) <- columns_sentiment
tail(joy, 10)

Similarly, let’s see other words that represents other 9 sentiments.

trust <- lyrics_words %>%
  inner_join(get_sentiments("nrc") %>% 
  filter(sentiment == "trust")) 
Joining, by = "word"
trust <- as.data.frame(sort(table(trust$word)))
colnames(trust) <- columns_sentiment
tail(trust, 10)
fear <- lyrics_words %>%
  inner_join(get_sentiments("nrc") %>% 
  filter(sentiment == "fear")) 
Joining, by = "word"
fear <- as.data.frame(sort(table(fear$word)))
colnames(fear) <- columns_sentiment
sadness <- lyrics_words %>%
  inner_join(get_sentiments("nrc") %>% 
  filter(sentiment == "sadness")) 
Joining, by = "word"
sadness <- as.data.frame(sort(table(sadness$word)))
colnames(sadness) <- columns_sentiment
anger <- lyrics_words %>%
  inner_join(get_sentiments("nrc") %>% 
  filter(sentiment == "anger")) 
Joining, by = "word"
anger <- as.data.frame(sort(table(anger$word)))
colnames(anger) <- columns_sentiment
surprise <- lyrics_words %>%
  inner_join(get_sentiments("nrc") %>% 
  filter(sentiment == "surprise")) 
Joining, by = "word"
surprise <- as.data.frame(sort(table(surprise$word)))
colnames(surprise) <- columns_sentiment
disgust <- lyrics_words %>%
  inner_join(get_sentiments("nrc") %>% 
  filter(sentiment == "disgust")) 
Joining, by = "word"
disgust <- as.data.frame(sort(table(disgust$word)))
colnames(disgust) <- columns_sentiment
anticipation <- lyrics_words %>%
  inner_join(get_sentiments("nrc") %>% 
  filter(sentiment == "anticipation")) 
Joining, by = "word"
anticipation <- as.data.frame(sort(table(anticipation$word)))
colnames(anticipation) <- columns_sentiment

Let’s plot the word occurences for each sentiment (except positive and negative)

Let us check top words that depicts “positive” and “negative” sentiments in NRC Sentiment category.

Joining, by = "word"
Joining, by = "word"

Let us check top words that depicts “positive” and “negative” sentiments in bing Sentiment category.

Joining, by = "word"
Joining, by = "word"
Joining, by = "word"
Joining, by = "word"

It can be seen that positive and negative words are different for all three lexicons.

LS0tCnRpdGxlOiAiTHlyaWNzIEFuYWx5c2lzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpMZXRzIHVzIHJlYWQgdGhlIGZpbGUuCgpgYGB7cn0KbGlicmFyeShyZWFkcikKbHlyaWNzIDwtIHJlYWRfY3N2KCJzb25nZGF0YS5jc3YiKQpoZWFkKGx5cmljcykKYGBgCgpMZXRzIHVzIGV4YW1pbmUgdGhlIGRpbWVuc2lvbiBvZiB0aGUgbHlyaWNzIGRhdGFmcmFtZS4KCmBgYHtyfQpkaW0obHlyaWNzKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KGRwbHlyKQpnbGltcHNlKGx5cmljcykKYGBgCgpBbmFseXNpcyBvZiA1NTAwMCsgbHlyaWNzIGRhdGEKLSBOdW1iZXIgb2YgYXJ0aXN0cwotIFdoaWNoIGFydGlzdCBoYXMgaGlnaGVzdCBhbmQgbG93ZXN0IG51bWJlciBvZiBzb25ncyAKLSBEaXN0cmlidXRpb24gb2Ygc29uZ3Mgb2YgYWxsIGFydGlzdHMgaW4gdGhlIGRhdGFzZXQKLSBEaXN0cmlidXRpb24gb2YgbHlyaWNzIGxlbmd0aAotIFdoaWNoIHNvbmcgbHlyaWNzIGhhcyBtYXhpbXVtIG51bWJlciBvZiB3b3JkcwotIFdoaWNoIHNvbmcgbHlyaWNzIGhhcyBtaW5pbXVtIG51bWJlciBvZiB3b3JkcwotIERpc3RyaWJ1dGlvbiBvZiB3b3JkcyBjb3VudCBpbiB0aXRsZQotIFdoaWNoIHNvbmdzIHRpdGxlIGhhcyBtYXhpbXVtIG51bWJlciBvZiB3b3JkcyAKLSBXaGljaCBzb25ncyB0aXRsZSBoYXMgbWluaW11bSBudW1iZXIgb2Ygd29yZHMKLSBXb3JkQ2xvdWRzIG9mIHRpdGxlcyB3aXRoIG1pbmltdW0gYW5kIG1heGltdW0gbGVuZ3RocwotIElzIHRoZXJlIGEgcmVsYXRpb24gYmV0d2VlbiB0aXRsZSBsZW5ndGggYW5kIHNvbmcgbGVuZ3RoPwoKCi0gU2VudGltZW50cyBvZiB0aGUgc29uZ3MgKE5SQywgQmluZykKLSBXaGljaCB3b3JkcyBhcmUgbW9zdCBvY2N1cmluZyBpbiB0aGUgbHlyaWNzIG9mIHRoZSBzb25ncwotIElzIHRoZXJlIGEgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgd29yZHMgaW4gdGhlIHNvbmdzIG9mIHNhbWUgYXJ0aXN0cz8KLSBXb3JkY2xvdWQgb2YgbW9zdCBwb3B1bGFyIHdvcmRzIGluIHRoZSBzb25ncwotIFRvcCB3b3JkcyB1c2VkIGJ5IGFuIGFydGlzdCBpbiBoaXMvaGVyIHNvbmdzCi0gQXJlIHRoZXJlIHNvbWUgY29tbW9uIFJ5dGhtaWMgd29yZHMgdGhhdCByZXBlYXRzIGFnYWluIGFuZCBhZ2Fpbj8KCgpMZXQncyBzdGFydCB3aXRoIGZpbmRpbmcgb3V0IGhvdyBtYW55IGFydGlzdHMgYXJlIGxpc3RlZCBpbiB0aGUgZGF0YS4gQWxzbywgaG93IG1hbnkgc29uZ3MgZWFjaCBhcnRpc3QgaGFzLgoKYGBge3J9CmFydGlzdDwtIGFzLmRhdGEuZnJhbWUodGFibGUoYXMuZGF0YS5mcmFtZShseXJpY3MkYXJ0aXN0KSkpCmNvbG5hbWVzKGFydGlzdCkgPC0gYygiYXJ0aXN0IiwgIk51bV9vZl9zb25ncyIpCmhlYWQoYXJ0aXN0KQpgYGAKCkxldCdzIHNlZSB0aGUgd2hpY2ggYXJ0aXN0IGhhcyBtb3N0IGFuZCBsZWFzdCBudW1iZXIgb2Ygc29uZ3MgaW4gdGhlIGRhdGFzZXQuCgpgYGB7cn0KbW9zdF9zb25ncyA8LSBhcnJhbmdlKGFydGlzdCwgZGVzYyhOdW1fb2Zfc29uZ3MpKQptb3N0X3NvbmdzCmBgYAoKYGBge3IgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9MywgZWNobz1GQUxTRX0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KFJtaXNjKQp0aWx0X3RoZW1lIDwtIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3Q9MSkpCnAxIDwtIGdncGxvdChkYXRhID0gaGVhZChtb3N0X3NvbmdzLDEwKSwgYWVzKGFydGlzdCwgTnVtX29mX3NvbmdzLCBmaWxsID0gTnVtX29mX3NvbmdzKSkgKwogICAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogICAgICBnZW9tX3RleHQoYWVzKGxhYmVsPU51bV9vZl9zb25ncyksIHZqdXN0PTEuNiwgY29sb3I9IndoaXRlIiwgc2l6ZT0zKSArCiAgICAgIGdndGl0bGUoIkFydGlzdHMgd2l0aCBtb3N0IG51bWJlciBvZiBzb25ncyIpICsKICAgICAgdGlsdF90aGVtZQpwMQpgYGAKCmBgYHtyfQpsZWFzdF9zb25ncyA8LSB0YWlsKG1vc3Rfc29uZ3MsIDE1KQpwMiA8LSBnZ3Bsb3QoZGF0YSA9IGxlYXN0X3NvbmdzLCBhZXMoYXJ0aXN0LCBOdW1fb2Zfc29uZ3MsIGZpbGwgPSBOdW1fb2Zfc29uZ3MpKSArCiAgICAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgICAgIGdlb21fdGV4dChhZXMobGFiZWw9TnVtX29mX3NvbmdzKSwgdmp1c3Q9MS42LCBjb2xvcj0id2hpdGUiLCBzaXplPTMpICsKICAgICAgZ2d0aXRsZSgiQXJ0aXN0cyB3aXRoIGxlYXN0IG51bWJlciBvZiBzb25ncyIpICsKICAgICAgdGlsdF90aGVtZQpwMgpgYGAKCkxldCdzIGNoZWNrIHRoZSBkaXN0cmlidXRpb24gb2Ygc29uZ3MgZm9yIGFsbCBhcnRpc3RzLgoKYGBge3J9CnAzIDwtIGdncGxvdChhcnRpc3QsIGFlcyh4PU51bV9vZl9zb25ncykpICsgCiBnZW9tX2hpc3RvZ3JhbShhZXMoeT0uLmRlbnNpdHkuLiksIGNvbG91cj0iYmxhY2siLCBmaWxsPSJ3aGl0ZSIpKwogZ2VvbV9kZW5zaXR5KGFscGhhPS4yLCBmaWxsPSJyZWQiKQpwMwpgYGAKCkxldCdzIGFuYWx5emUgdGhlIG51bWJlciBvZiB3b3JkcyBpbiBlYWNoIHNvbmcgYW5kIGl0cyBkaXN0cmlidXRpb24uCgpgYGB7cn0KbGlicmFyeShzdHJpbmdyKQpjb3VudF93b3JkcyA8LSBmdW5jdGlvbih2ZWMpewogIHJldHVybiAobGVuZ3RoKHVubGlzdCgoc3RyX2V4dHJhY3RfYWxsKHRvbG93ZXIodmVjKSwgJ1xcdysnKSkpKSkKfQpseXJpY3Mkd29yZF9jb3VudCA8LSBzYXBwbHkobHlyaWNzJHRleHQsIGNvdW50X3dvcmRzKQpoZWFkKGx5cmljcyR3b3JkX2NvdW50KQpgYGAKCmBgYHtyfQpwNCA8LSBnZ3Bsb3QobHlyaWNzLCBhZXMoeD13b3JkX2NvdW50KSkgKyAKIGdlb21faGlzdG9ncmFtKGFlcyh5PS4uZGVuc2l0eS4uKSwgY29sb3VyPSJibGFjayIsIGZpbGw9IndoaXRlIikrCiBnZW9tX2RlbnNpdHkoYWxwaGE9LjIsIGZpbGw9InJlZCIpCnA0CmBgYAoKTGV0J3MgYW5hbHl6ZSB0aGUgdGl0bGUgb2YgdGhlIHNvbmdzLCB0aGVpciB3b3JkY291bnQgYW5kIHRoZWlyIGRpc3RyaWJ1dGlvbgoKYGBge3J9Cmx5cmljcyR0aXRsZV93b3JkX2NvdW50IDwtIHNhcHBseShseXJpY3Mkc29uZywgY291bnRfd29yZHMpCmhlYWQobHlyaWNzJHRpdGxlX3dvcmRfY291bnQpCmBgYAoKTGV0J3MgY2hlY2sgb3V0IHRoZSBzb25ncyB0aGF0IGFyZSBsb25nZXN0IGFuZCBzaG9ydGVzdC4KCmBgYHtyfQpsb25nZXN0X3NvbmcgPC0gYXJyYW5nZShseXJpY3MsIGRlc2Mod29yZF9jb3VudCkpCmxvbmdlc3Rfc29uZyA8LSBoZWFkKGxvbmdlc3Rfc29uZywgMTApCnNob3J0ZXN0X3NvbmcgPC0gYXJyYW5nZShseXJpY3MsIHdvcmRfY291bnQpCnNob3J0ZXN0X3NvbmcgPC0gaGVhZChzaG9ydGVzdF9zb25nLCAxMCkKbG9uZ2VzdF9zb25nCnNob3J0ZXN0X3NvbmcKYGBgCgoKCmBgYHtyfQpwNSA8LSBnZ3Bsb3QoZGF0YSA9IGxvbmdlc3Rfc29uZywgYWVzKHNvbmcsIHdvcmRfY291bnQsIGZpbGwgPSB0aXRsZV93b3JkX2NvdW50KSkgKwogICAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogICAgICBnZW9tX3RleHQoYWVzKGxhYmVsPXRpdGxlX3dvcmRfY291bnQpLCB2anVzdD0xLjYsIGNvbG9yPSJ3aGl0ZSIsIHNpemU9MykgKwogICAgICBnZ3RpdGxlKCJMb25nZXN0IFNvbmdzIikgKwogICAgICB0aWx0X3RoZW1lCnA2IDwtIGdncGxvdChkYXRhID0gc2hvcnRlc3Rfc29uZywgYWVzKHNvbmcsIHdvcmRfY291bnQsIGZpbGwgPSB0aXRsZV93b3JkX2NvdW50KSkgKwogICAgICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJkb2RnZSIsIHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgICAgIGdlb21fdGV4dChhZXMobGFiZWwgPSB0aXRsZV93b3JkX2NvdW50KSwgdmp1c3QgPSAxLjYsIGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDMpICsKICAgICAgZ2d0aXRsZSgiU2hvcnRlc3QgU29uZ3MiKSArCiAgICAgIHRpbHRfdGhlbWUKbXVsdGlwbG90KHA1LCBwNiwgY29scz0yKQpgYGAKCgoKCmBgYHtyfQpwNyA8LSBnZ3Bsb3QobHlyaWNzLCBhZXMoeD10aXRsZV93b3JkX2NvdW50KSkgKyAKIGdlb21faGlzdG9ncmFtKGFlcyh5PS4uZGVuc2l0eS4uKSwgY29sb3VyPSJibGFjayIsIGZpbGw9IndoaXRlIiwgYmlud2lkdGggPSAxLCBiaW5zID0gMSkrCiBnZW9tX2RlbnNpdHkoYWxwaGE9LjIsIGZpbGw9InJlZCIpCnA3CmBgYAoKV29yZENsb3VkIG9mIHBvcHVsYXIgd29yZHMgZnJvbSBzb25nIHRpdGxlcwoKYGBge3J9CmxpYnJhcnkod29yZGNsb3VkKQpsaWJyYXJ5KFNub3diYWxsQykKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmxpYnJhcnkodG0pCnRleHRzIDwtIGx5cmljcyRzb25nCiN0ZXh0cyA8LSBpY29udih0ZXh0cywgdG8gPSAidXRmLTgiKQpjb3JwdXMgPC0gQ29ycHVzKFZlY3RvclNvdXJjZSh0ZXh0cykpCmNvcnB1cyA8LSB0bV9tYXAoY29ycHVzLCBQbGFpblRleHREb2N1bWVudCkKY29ycHVzIDwtIHRtX21hcChjb3JwdXMsIHJlbW92ZVB1bmN0dWF0aW9uKQpjb3JwdXMgPC0gdG1fbWFwKGNvcnB1cywgcmVtb3ZlV29yZHMsIHN0b3B3b3JkcygnZW5nbGlzaCcpKQpjb3JwdXMgPC0gdG1fbWFwKGNvcnB1cywgc3RlbURvY3VtZW50KQpjb3JwdXMgPC0gdG1fbWFwKGNvcnB1cywgcmVtb3ZlV29yZHMsIGMoImFuZCIsICJ0aGlzIiwgInRoZXJlIikpIApjb3JwdXMgPC0gQ29ycHVzKFZlY3RvclNvdXJjZShjb3JwdXMpKQpkdG0gPC0gVGVybURvY3VtZW50TWF0cml4KGNvcnB1cykKbSA8LSBhcy5tYXRyaXgoZHRtKQp2IDwtIHNvcnQocm93U3VtcyhtKSxkZWNyZWFzaW5nPVRSVUUpCmQgPC0gZGF0YS5mcmFtZSh3b3JkID0gbmFtZXModiksZnJlcT12KQpoZWFkKGQsIDEwKQpkIDwtIGRbLXdoaWNoKGQkd29yZCAlaW4lIGMoImFuZCIsInRoaXMiLCJ0aGF0IikpLF0Kc2V0LnNlZWQoMTIzNCkKd29yZGNsb3VkKHdvcmRzID0gZCR3b3JkLCBmcmVxID0gZCRmcmVxLCBtaW4uZnJlcSA9IDEsCiAgICAgICAgICBtYXgud29yZHM9MjAwLCByYW5kb20ub3JkZXI9RkFMU0UsIHJvdC5wZXI9MC4zNSwgCiAgICAgICAgICBjb2xvcnM9YnJld2VyLnBhbCg4LCAiRGFyazIiKSkKYGBgCgpUaGVyZSBhcmUgbWFueSBzb25nIHRpdGxlcyB0aGF0IGFyZSBvZiBsZW5ndGggMSwgMiBhbmQgMy4gQnV0IHN1cnByaXNpbmdseSwgdGhlcmUgYXJlIHRpdGxlcyBvZiBsZW5ndGggbW9yZSB0aGFuIDEzIHRvby4gTGV0J3MgY2hlY2sgdGhlbSBvdXQuCgpgYGB7cn0KbG9uZ2VzdF90aXRsZSA8LSBzdWJzZXQobHlyaWNzLCBseXJpY3MkdGl0bGVfd29yZF9jb3VudCA+IDEzKQpsb25nZXN0X3RpdGxlCnNob3J0ZXN0X3RpdGxlIDwtIHN1YnNldChseXJpY3MsIGx5cmljcyR0aXRsZV93b3JkX2NvdW50ID09IDEpCnNob3J0ZXN0X3RpdGxlCmBgYAoKVGhlcmUgYXJlIDggc29uZ3Mgd2l0aCB0aXRsZSBsZW5ndGggbW9yZSB0aGFuIDEzIGFuZCA4MzQyIHNvbmdzIHdpdGggc2luZ2xlIHdvcmQgdGl0bGUuIExldCdzIHNlZSB3b3JkIGNsb3VkIG9mIHNpbmdsZSB3b3JkIHRpdGxlcyBhbmQgbG9uZ2VzdCB0aXRsZXMKCmBgYHtyfQp0ZXh0cyA8LSBsb25nZXN0X3RpdGxlJHNvbmcKY29ycHVzIDwtIENvcnB1cyhWZWN0b3JTb3VyY2UodGV4dHMpKQpjb3JwdXMgPC0gdG1fbWFwKGNvcnB1cywgUGxhaW5UZXh0RG9jdW1lbnQpCmNvcnB1cyA8LSBDb3JwdXMoVmVjdG9yU291cmNlKGNvcnB1cykpCmR0bSA8LSBUZXJtRG9jdW1lbnRNYXRyaXgoY29ycHVzKQptIDwtIGFzLm1hdHJpeChkdG0pCnYgPC0gc29ydChyb3dTdW1zKG0pLGRlY3JlYXNpbmc9VFJVRSkKZCA8LSBkYXRhLmZyYW1lKHdvcmQgPSBuYW1lcyh2KSxmcmVxPXYpCmhlYWQoZCwgMTApCnNldC5zZWVkKDEyMzQpCndvcmRjbG91ZCh3b3JkcyA9IGQkd29yZCwgZnJlcSA9IGQkZnJlcSwgbWluLmZyZXEgPSAxLHNjYWxlPWMoMiwwLjUpLAogICAgICAgICAgbWF4LndvcmRzPTEwMCwgcmFuZG9tLm9yZGVyPUZBTFNFLCByb3QucGVyPTAuMzUsIAogICAgICAgICAgY29sb3JzPWJyZXdlci5wYWwoOCwgIkRhcmsyIikpCmBgYAoKYGBge3J9CnRleHRzIDwtIHNob3J0ZXN0X3RpdGxlJHNvbmcKY29ycHVzIDwtIENvcnB1cyhWZWN0b3JTb3VyY2UodGV4dHMpKQpjb3JwdXMgPC0gdG1fbWFwKGNvcnB1cywgUGxhaW5UZXh0RG9jdW1lbnQpCmNvcnB1cyA8LSBDb3JwdXMoVmVjdG9yU291cmNlKGNvcnB1cykpCmR0bSA8LSBUZXJtRG9jdW1lbnRNYXRyaXgoY29ycHVzKQptIDwtIGFzLm1hdHJpeChkdG0pCnYgPC0gc29ydChyb3dTdW1zKG0pLGRlY3JlYXNpbmc9VFJVRSkKZCA8LSBkYXRhLmZyYW1lKHdvcmQgPSBuYW1lcyh2KSxmcmVxPXYpCmhlYWQoZCwgMTApCnNldC5zZWVkKDEyMzQpCndvcmRjbG91ZCh3b3JkcyA9IGQkd29yZCwgZnJlcSA9IGQkZnJlcSwgbWluLmZyZXEgPSAxLHNjYWxlPWMoMiwwLjUpLAogICAgICAgICAgbWF4LndvcmRzPTEwMCwgcmFuZG9tLm9yZGVyPUZBTFNFLCByb3QucGVyPTAuMzUsIAogICAgICAgICAgY29sb3JzPWJyZXdlci5wYWwoOCwgIkRhcmsyIikpCmBgYAoKQW4gaW50ZXJlc3RpbmcgcXVlc3RpbiB3b3VsZCBiZSBpcyB0aGVyZSByZWxhdGlvbiBiZXR3ZWVuIGxlbmd0aCBvZiB0aXRsZSBhbmQgc29uZ3M/IE1vc3QgcHJvYmFibHkgbm93LCBidXQgbGV0J3MgY2hlY2sgb3V0LgoKYGBge3J9CnA4IDwtIGdncGxvdChseXJpY3MsIGFlcyh4PWZhY3Rvcih0aXRsZV93b3JkX2NvdW50KSwgeT13b3JkX2NvdW50LCBmaWxsID0gZmFjdG9yKHRpdGxlX3dvcmRfY291bnQpKSkgKyAKICBnZW9tX2JveHBsb3QoKSAKcDggCmBgYAoKYGBge3J9CmNvcihseXJpY3MkdGl0bGVfd29yZF9jb3VudCwgbHlyaWNzJHdvcmRfY291bnQpCmBgYAogQXMgZXhwZWN0ZWQsIHRoZXJlIGlzIG5vIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlc2UgdHdvIHF1YW50aXRpdGVzLgogCiBMZXQgdXMgZml4IHRoZSBjb250cmFjdGVkIHdvcmRzIHRvIHRoZWlyIGZ1bGwgZm9ybXMgZmlyc3QuCgpgYGB7cn0KIyBmdW5jdGlvbiB0byBleHBhbmQgY29udHJhY3Rpb25zIGluIGFuIEVuZ2xpc2gtbGFuZ3VhZ2Ugc291cmNlCmZpeC5jb250cmFjdGlvbnMgPC0gZnVuY3Rpb24oZG9jKSB7CiAgIyAid29uJ3QiIGlzIGEgc3BlY2lhbCBjYXNlIGFzIGl0IGRvZXMgbm90IGV4cGFuZCB0byAid28gbm90IgogIGRvYyA8LSBnc3ViKCJ3b24ndCIsICJ3aWxsIG5vdCIsIGRvYykKICBkb2MgPC0gZ3N1YigiY2FuJ3QiLCAiY2FuIG5vdCIsIGRvYykKICBkb2MgPC0gZ3N1Yigibid0IiwgIiBub3QiLCBkb2MpCiAgZG9jIDwtIGdzdWIoIidsbCIsICIgd2lsbCIsIGRvYykKICBkb2MgPC0gZ3N1YigiJ3JlIiwgIiBhcmUiLCBkb2MpCiAgZG9jIDwtIGdzdWIoIid2ZSIsICIgaGF2ZSIsIGRvYykKICBkb2MgPC0gZ3N1YigiJ20iLCAiIGFtIiwgZG9jKQogIGRvYyA8LSBnc3ViKCInZCIsICIgd291bGQiLCBkb2MpCiAgIyAncyBjb3VsZCBiZSAnaXMnIG9yIGNvdWxkIGJlIHBvc3Nlc3NpdmU6IGl0IGhhcyBubyBleHBhbnNpb24KICBkb2MgPC0gZ3N1YigiJ3MiLCAiIiwgZG9jKQogIHJldHVybihkb2MpCn0KCiMgZml4IChleHBhbmQpIGNvbnRyYWN0aW9ucwpseXJpY3MkdGV4dCA8LSBzYXBwbHkobHlyaWNzJHRleHQsIGZpeC5jb250cmFjdGlvbnMpCmBgYAogCiBSZW1vdmUgc3BlY2lhbCBjaGFyYWN0ZXJzIGZyb20gbHlyaWNzCmBgYHtyfQojIGZ1bmN0aW9uIHRvIHJlbW92ZSBzcGVjaWFsIGNoYXJhY3RlcnMKcmVtb3ZlU3BlY2lhbENoYXJzIDwtIGZ1bmN0aW9uKHgpIGdzdWIoIlteYS16QS1aMC05IF0iLCAiICIsIHgpCiMgcmVtb3ZlIHNwZWNpYWwgY2hhcmFjdGVycwpseXJpY3MkdGV4dCA8LSBzYXBwbHkobHlyaWNzJHRleHQsIHJlbW92ZVNwZWNpYWxDaGFycykKYGBgCgpDb252ZXJ0IGFsbCBseXJpY3MgdGV4dCB0byBsb3dlciBjYXNlCmBgYHtyfQojIGNvbnZlcnQgZXZlcnl0aGluZyB0byBsb3dlciBjYXNlCmx5cmljcyR0ZXh0IDwtIHNhcHBseShseXJpY3MkdGV4dCwgdG9sb3dlcikKYGBgCgpMZXQncyBjaGVjayB0aGUgc3RydWN0dXJlIG9mIG9uZSBseXJpY3MgdG8gc2VlIHRoZSBjaGFuZ2VzLgpgYGB7cn0Kc3RyKGx5cmljc1sxMywgXSR0ZXh0LCBuY2hhci5tYXggPSAzMDApCmBgYAogCiBTRU5USU1FTlQgQU5BTFlTSVMgT0YgTFlSSUNTCiBMZXQgdXMgcGVyZm9ybSBzZW50aW1lbnQgYW5hbHlzaXMgb24gdGhlIGx5cmljcy4gVGhlcmUgYXJlIHZhcmlvdXMgdHlwZXMgb2Ygc2VudGltZW50IGxleGljb25zIHRoYXQgY2FuIGJlIHVzZWQuIExldHMgdXMgaGF2ZSBhIGxvb2sgb24gdGhlbS4KYGBge3J9CmxpYnJhcnkodGlkeXRleHQpCmxpYnJhcnkodGlkeXIpCmdldF9zZW50aW1lbnRzKCJhZmlubiIpCmdldF9zZW50aW1lbnRzKCJiaW5nIikKZ2V0X3NlbnRpbWVudHMoIm5yYyIpCmdldF9zZW50aW1lbnRzKCJsb3VnaHJhbiIpCmBgYAoKbnJjIHNlZW1zIHRvIGhhdmUgbGFyZ2UgbnVtYmVyIG9mIHdvcmRzIGFuZCB0aGVpciBzZW50aW1lbnRzIGNvbXBhcmVkIHRvIG90aGVyIHR3by4KCmBgYHtyfQpucmNfc2VudGltZW50IDwtIGdldF9zZW50aW1lbnRzKCJucmMiKQp1bmlxdWUobnJjX3NlbnRpbWVudCRzZW50aW1lbnQpCmBgYAoKTGV0IHVzIGZpbmQgdGhlIHNlbnRpbWVudHMgb2YgZWFjaCBseXJpY3MgYmFzZWQgb24gZWFjaCBOUkMgc2VudGltZW50cy4KCmBgYHtyfQpseXJpY3Nfd29yZHMgPC0gc2VsZWN0KGx5cmljcywgYygiYXJ0aXN0IiwgInRleHQiKSkKbHlyaWNzX3dvcmRzIDwtIGx5cmljc193b3JkcyAlPiUgdW5uZXN0X3Rva2Vucyh3b3JkLCB0ZXh0KQpoZWFkKGx5cmljc193b3JkcykKZGltKGx5cmljc193b3JkcykKYGBgCgpMZXQncyBzZWUgd29yZHMgdGhhdCBkZXBpY3QgImpveSIKYGBge3J9CmpveSA8LSBseXJpY3Nfd29yZHMgJT4lCiAgaW5uZXJfam9pbihnZXRfc2VudGltZW50cygibnJjIikgJT4lIAogIGZpbHRlcihzZW50aW1lbnQgPT0gImpveSIpKSAKam95IDwtIGFzLmRhdGEuZnJhbWUoc29ydCh0YWJsZShqb3kkd29yZCkpKQpjb2x1bW5zX3NlbnRpbWVudCA8LSBjKCJ3b3JkIiwgIkZyZXEiKQpjb2xuYW1lcyhqb3kpIDwtIGNvbHVtbnNfc2VudGltZW50CnRhaWwoam95LCAxMCkKCmBgYAoKU2ltaWxhcmx5LCBsZXQncyBzZWUgb3RoZXIgd29yZHMgdGhhdCByZXByZXNlbnRzIG90aGVyIDkgc2VudGltZW50cy4KCmBgYHtyfQp0cnVzdCA8LSBseXJpY3Nfd29yZHMgJT4lCiAgaW5uZXJfam9pbihnZXRfc2VudGltZW50cygibnJjIikgJT4lIAogIGZpbHRlcihzZW50aW1lbnQgPT0gInRydXN0IikpIAp0cnVzdCA8LSBhcy5kYXRhLmZyYW1lKHNvcnQodGFibGUodHJ1c3Qkd29yZCkpKQpjb2xuYW1lcyh0cnVzdCkgPC0gY29sdW1uc19zZW50aW1lbnQKdGFpbCh0cnVzdCwgMTApCmBgYAoKYGBge3J9CmZlYXIgPC0gbHlyaWNzX3dvcmRzICU+JQogIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoIm5yYyIpICU+JSAKICBmaWx0ZXIoc2VudGltZW50ID09ICJmZWFyIikpIApmZWFyIDwtIGFzLmRhdGEuZnJhbWUoc29ydCh0YWJsZShmZWFyJHdvcmQpKSkKY29sbmFtZXMoZmVhcikgPC0gY29sdW1uc19zZW50aW1lbnQKCnNhZG5lc3MgPC0gbHlyaWNzX3dvcmRzICU+JQogIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoIm5yYyIpICU+JSAKICBmaWx0ZXIoc2VudGltZW50ID09ICJzYWRuZXNzIikpIApzYWRuZXNzIDwtIGFzLmRhdGEuZnJhbWUoc29ydCh0YWJsZShzYWRuZXNzJHdvcmQpKSkKY29sbmFtZXMoc2FkbmVzcykgPC0gY29sdW1uc19zZW50aW1lbnQKCmFuZ2VyIDwtIGx5cmljc193b3JkcyAlPiUKICBpbm5lcl9qb2luKGdldF9zZW50aW1lbnRzKCJucmMiKSAlPiUgCiAgZmlsdGVyKHNlbnRpbWVudCA9PSAiYW5nZXIiKSkgCmFuZ2VyIDwtIGFzLmRhdGEuZnJhbWUoc29ydCh0YWJsZShhbmdlciR3b3JkKSkpCmNvbG5hbWVzKGFuZ2VyKSA8LSBjb2x1bW5zX3NlbnRpbWVudAoKc3VycHJpc2UgPC0gbHlyaWNzX3dvcmRzICU+JQogIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoIm5yYyIpICU+JSAKICBmaWx0ZXIoc2VudGltZW50ID09ICJzdXJwcmlzZSIpKSAKc3VycHJpc2UgPC0gYXMuZGF0YS5mcmFtZShzb3J0KHRhYmxlKHN1cnByaXNlJHdvcmQpKSkKY29sbmFtZXMoc3VycHJpc2UpIDwtIGNvbHVtbnNfc2VudGltZW50CgpkaXNndXN0IDwtIGx5cmljc193b3JkcyAlPiUKICBpbm5lcl9qb2luKGdldF9zZW50aW1lbnRzKCJucmMiKSAlPiUgCiAgZmlsdGVyKHNlbnRpbWVudCA9PSAiZGlzZ3VzdCIpKSAKZGlzZ3VzdCA8LSBhcy5kYXRhLmZyYW1lKHNvcnQodGFibGUoZGlzZ3VzdCR3b3JkKSkpCmNvbG5hbWVzKGRpc2d1c3QpIDwtIGNvbHVtbnNfc2VudGltZW50CgphbnRpY2lwYXRpb24gPC0gbHlyaWNzX3dvcmRzICU+JQogIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoIm5yYyIpICU+JSAKICBmaWx0ZXIoc2VudGltZW50ID09ICJhbnRpY2lwYXRpb24iKSkgCmFudGljaXBhdGlvbiA8LSBhcy5kYXRhLmZyYW1lKHNvcnQodGFibGUoYW50aWNpcGF0aW9uJHdvcmQpKSkKY29sbmFtZXMoYW50aWNpcGF0aW9uKSA8LSBjb2x1bW5zX3NlbnRpbWVudApgYGAKCkxldCdzIHBsb3QgdGhlIHdvcmQgb2NjdXJlbmNlcyBmb3IgZWFjaCBzZW50aW1lbnQgKGV4Y2VwdCBwb3NpdGl2ZSBhbmQgbmVnYXRpdmUpCgpgYGB7ciBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD00LCBlY2hvPUZBTFNFfQpwOSA8LSBnZ3Bsb3QoZGF0YSA9IHRhaWwoam95LCAxMCksIGFlcyh3b3JkLCBGcmVxLCBmaWxsID0gd29yZCkpICsKICAgICAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiLCBzdGF0ID0gImlkZW50aXR5IikgKwogICAgICBnZW9tX3RleHQoYWVzKGxhYmVsID0gRnJlcSksIHZqdXN0ID0gMS42LCBjb2xvciA9ICJ3aGl0ZSIsIHNpemUgPSAzKSArCiAgICAgIGdndGl0bGUoIkpveSIpICsKICAgICAgZ3VpZGVzKGZpbGw9RkFMU0UpICsKICAgICAgdGlsdF90aGVtZQpwMTAgPC0gZ2dwbG90KGRhdGEgPSB0YWlsKHRydXN0LCAxMCksIGFlcyh3b3JkLCBGcmVxLCBmaWxsID0gd29yZCkpICsKICAgICAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiLCBzdGF0ID0gImlkZW50aXR5IikgKwogICAgICBnZW9tX3RleHQoYWVzKGxhYmVsID0gRnJlcSksIHZqdXN0ID0gMS42LCBjb2xvciA9ICJ3aGl0ZSIsIHNpemUgPSAzKSArCiAgICAgIGdndGl0bGUoIlRydXN0IikgKwogICAgICBndWlkZXMoZmlsbD1GQUxTRSkgKwogICAgICB0aWx0X3RoZW1lCnAxMSA8LSBnZ3Bsb3QoZGF0YSA9IHRhaWwoZmVhciwgMTApLCBhZXMod29yZCwgRnJlcSwgZmlsbCA9IHdvcmQpKSArCiAgICAgIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIiwgc3RhdCA9ICJpZGVudGl0eSIpICsKICAgICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IEZyZXEpLCB2anVzdCA9IDEuNiwgY29sb3IgPSAid2hpdGUiLCBzaXplID0gMykgKwogICAgICBnZ3RpdGxlKCJGZWFyIikgKwogICAgICBndWlkZXMoZmlsbD1GQUxTRSkgKwogICAgICB0aWx0X3RoZW1lCnAxMiA8LSBnZ3Bsb3QoZGF0YSA9IHRhaWwoc2FkbmVzcywgMTApLCBhZXMod29yZCwgRnJlcSwgZmlsbCA9IHdvcmQpKSArCiAgICAgIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIiwgc3RhdCA9ICJpZGVudGl0eSIpICsKICAgICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IEZyZXEpLCB2anVzdCA9IDEuNiwgY29sb3IgPSAid2hpdGUiLCBzaXplID0gMykgKwogICAgICBnZ3RpdGxlKCJTYWRuZXNzIikgKwogICAgICBndWlkZXMoZmlsbD1GQUxTRSkgKwogICAgICB0aWx0X3RoZW1lCnAxMyA8LSBnZ3Bsb3QoZGF0YSA9IHRhaWwoYW5nZXIsIDEwKSwgYWVzKHdvcmQsIEZyZXEsIGZpbGwgPSB3b3JkKSkgKwogICAgICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJkb2RnZSIsIHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgICAgIGdlb21fdGV4dChhZXMobGFiZWwgPSBGcmVxKSwgdmp1c3QgPSAxLjYsIGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDMpICsKICAgICAgZ2d0aXRsZSgiQW5nZXIiKSArCiAgICAgIGd1aWRlcyhmaWxsPUZBTFNFKSArCiAgICAgIHRpbHRfdGhlbWUKcDE0IDwtIGdncGxvdChkYXRhID0gdGFpbChzdXJwcmlzZSwgMTApLCBhZXMod29yZCwgRnJlcSwgZmlsbCA9IHdvcmQpKSArCiAgICAgIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIiwgc3RhdCA9ICJpZGVudGl0eSIpICsKICAgICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IEZyZXEpLCB2anVzdCA9IDEuNiwgY29sb3IgPSAid2hpdGUiLCBzaXplID0gMykgKwogICAgICBnZ3RpdGxlKCJTdXJwcmlzZSIpICsKICAgICAgZ3VpZGVzKGZpbGw9RkFMU0UpICsKICAgICAgdGlsdF90aGVtZQpwMTUgPC0gZ2dwbG90KGRhdGEgPSB0YWlsKGRpc2d1c3QsIDEwKSwgYWVzKHdvcmQsIEZyZXEsIGZpbGwgPSB3b3JkKSkgKwogICAgICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJkb2RnZSIsIHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgICAgIGdlb21fdGV4dChhZXMobGFiZWwgPSBGcmVxKSwgdmp1c3QgPSAxLjYsIGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDMpICsKICAgICAgZ2d0aXRsZSgiRGlzZ3VzdCIpICsKICAgICAgZ3VpZGVzKGZpbGw9RkFMU0UpICsKICAgICAgdGlsdF90aGVtZQpwMTYgPC0gZ2dwbG90KGRhdGEgPSB0YWlsKGFudGljaXBhdGlvbiwgMTApLCBhZXMod29yZCwgRnJlcSwgZmlsbCA9IHdvcmQpKSArCiAgICAgIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIiwgc3RhdCA9ICJpZGVudGl0eSIpICsKICAgICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IEZyZXEpLCB2anVzdCA9IDEuNiwgY29sb3IgPSAid2hpdGUiLCBzaXplID0gMykgKwogICAgICBnZ3RpdGxlKCJBbnRpY2lwYXRpb24iKSArCiAgICAgIGd1aWRlcyhmaWxsPUZBTFNFKSArCiAgICAgIHRpbHRfdGhlbWUKCm11bHRpcGxvdChwOSwgcDEwLCBwMTEsIHAxMiwgcDEzLCBwMTQsIHAxNSwgcDE2LCBsYXlvdXQgPSBtYXRyaXgoYygxLDIsMyw0LDUsNiw3LDgpLCBucm93PTIsIGJ5cm93PVRSVUUpKQoKCmBgYAoKTGV0IHVzIGNoZWNrIHRvcCB3b3JkcyB0aGF0IGRlcGljdHMgInBvc2l0aXZlIiBhbmQgIm5lZ2F0aXZlIiBzZW50aW1lbnRzIGluIE5SQyBTZW50aW1lbnQgY2F0ZWdvcnkuCgpgYGB7ciBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD0yLCBlY2hvPUZBTFNFfQpwb3MgPC0gbHlyaWNzX3dvcmRzICU+JQogIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoIm5yYyIpICU+JSAKICBmaWx0ZXIoc2VudGltZW50ID09ICJwb3NpdGl2ZSIpKSAKcG9zIDwtIGFzLmRhdGEuZnJhbWUoc29ydCh0YWJsZShwb3Mkd29yZCkpKQpjb2xuYW1lcyhwb3MpIDwtIGNvbHVtbnNfc2VudGltZW50CgpuZWcgPC0gbHlyaWNzX3dvcmRzICU+JQogIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoIm5yYyIpICU+JSAKICBmaWx0ZXIoc2VudGltZW50ID09ICJuZWdhdGl2ZSIpKSAKbmVnIDwtIGFzLmRhdGEuZnJhbWUoc29ydCh0YWJsZShuZWckd29yZCkpKQpjb2xuYW1lcyhuZWcpIDwtIGNvbHVtbnNfc2VudGltZW50CgpwMTcgPC0gZ2dwbG90KGRhdGEgPSB0YWlsKHBvcywgMjApLCBhZXMod29yZCwgRnJlcSwgZmlsbCA9IHdvcmQpKSArCiAgICAgIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIiwgc3RhdCA9ICJpZGVudGl0eSIpICsKICAgICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IEZyZXEpLCB2anVzdCA9IDEuNiwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMywgYW5nbGU9OTApICsKICAgICAgZ2d0aXRsZSgiUG9zaXRpdmUgKE5SQykiKSArCiAgICAgIGd1aWRlcyhmaWxsPUZBTFNFKSArCiAgICAgIHRpbHRfdGhlbWUKcDE4IDwtIGdncGxvdChkYXRhID0gdGFpbChuZWcsIDIwKSwgYWVzKHdvcmQsIEZyZXEsIGZpbGwgPSB3b3JkKSkgKwogICAgICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJkb2RnZSIsIHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgICAgIGdlb21fdGV4dChhZXMobGFiZWwgPSBGcmVxKSwgdmp1c3QgPSAxLjYsIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDMsIGFuZ2xlPTkwKSArCiAgICAgIGdndGl0bGUoIk5lZ2F0aXZlIChOUkMpIikgKwogICAgICBndWlkZXMoZmlsbD1GQUxTRSkgKwogICAgICB0aWx0X3RoZW1lCm11bHRpcGxvdChwMTcsIHAxOCwgY29scz0yKQpgYGAKCkxldCB1cyBjaGVjayB0b3Agd29yZHMgdGhhdCBkZXBpY3RzICJwb3NpdGl2ZSIgYW5kICJuZWdhdGl2ZSIgc2VudGltZW50cyBpbiBiaW5nIFNlbnRpbWVudCBjYXRlZ29yeS4KCmBgYHtyIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD01LCBlY2hvPUZBTFNFfQpwb3NfYiA8LSBseXJpY3Nfd29yZHMgJT4lCiAgaW5uZXJfam9pbihnZXRfc2VudGltZW50cygiYmluZyIpICU+JSAKICBmaWx0ZXIoc2VudGltZW50ID09ICJwb3NpdGl2ZSIpKSAKcG9zX2IgPC0gYXMuZGF0YS5mcmFtZShzb3J0KHRhYmxlKHBvc19iJHdvcmQpKSkKY29sbmFtZXMocG9zX2IpIDwtIGNvbHVtbnNfc2VudGltZW50CgpuZWdfYiA8LSBseXJpY3Nfd29yZHMgJT4lCiAgaW5uZXJfam9pbihnZXRfc2VudGltZW50cygiYmluZyIpICU+JSAKICBmaWx0ZXIoc2VudGltZW50ID09ICJuZWdhdGl2ZSIpKSAKbmVnX2IgPC0gYXMuZGF0YS5mcmFtZShzb3J0KHRhYmxlKG5lZ19iJHdvcmQpKSkKY29sbmFtZXMobmVnX2IpIDwtIGNvbHVtbnNfc2VudGltZW50Cgpwb3NfbCA8LSBseXJpY3Nfd29yZHMgJT4lIAogIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoImxvdWdocmFuIikgJT4lIAogIGZpbHRlcihzZW50aW1lbnQgPT0gInBvc2l0aXZlIikpIApwb3NfbCA8LSBhcy5kYXRhLmZyYW1lKHNvcnQodGFibGUocG9zX2wkd29yZCkpKQpjb2xuYW1lcyhwb3NfbCkgPC0gY29sdW1uc19zZW50aW1lbnQKCm5lZ19sIDwtIGx5cmljc193b3JkcyAlPiUKICBpbm5lcl9qb2luKGdldF9zZW50aW1lbnRzKCJsb3VnaHJhbiIpICU+JSAKICBmaWx0ZXIoc2VudGltZW50ID09ICJuZWdhdGl2ZSIpKSAKbmVnX2wgPC0gYXMuZGF0YS5mcmFtZShzb3J0KHRhYmxlKG5lZ19sJHdvcmQpKSkKY29sbmFtZXMobmVnX2wpIDwtIGNvbHVtbnNfc2VudGltZW50CgpwMTkgPC0gZ2dwbG90KGRhdGEgPSB0YWlsKHBvc19iLCAyMCksIGFlcyh3b3JkLCBGcmVxLCBmaWxsID0gd29yZCkpICsKICAgICAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiLCBzdGF0ID0gImlkZW50aXR5IikgKwogICAgICBnZW9tX3RleHQoYWVzKGxhYmVsID0gRnJlcSksIHZqdXN0ID0gMS42LCBjb2xvciA9ICJibGFjayIsIHNpemUgPSAzLCBhbmdsZT05MCkgKwogICAgICBnZ3RpdGxlKCJQb3NpdGl2ZSAoQklORykiKSArCiAgICAgIGd1aWRlcyhmaWxsPUZBTFNFKSArCiAgICAgIHRpbHRfdGhlbWUKcDIwIDwtIGdncGxvdChkYXRhID0gdGFpbChuZWdfYiwgMjApLCBhZXMod29yZCwgRnJlcSwgZmlsbCA9IHdvcmQpKSArCiAgICAgIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIiwgc3RhdCA9ICJpZGVudGl0eSIpICsKICAgICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IEZyZXEpLCB2anVzdCA9IDEuNiwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMywgYW5nbGU9OTApICsKICAgICAgZ2d0aXRsZSgiTmVnYXRpdmUgKEJJTkcpIikgKwogICAgICBndWlkZXMoZmlsbD1GQUxTRSkgKwogICAgICB0aWx0X3RoZW1lCnAyMSA8LSBnZ3Bsb3QoZGF0YSA9IHRhaWwocG9zX2wsIDIwKSwgYWVzKHdvcmQsIEZyZXEsIGZpbGwgPSB3b3JkKSkgKwogICAgICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJkb2RnZSIsIHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgICAgIGdlb21fdGV4dChhZXMobGFiZWwgPSBGcmVxKSwgdmp1c3QgPSAxLjYsIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDMsIGFuZ2xlPTkwKSArCiAgICAgIGdndGl0bGUoIlBvc2l0aXZlIChMT1VHSFJBTikiKSArCiAgICAgIGd1aWRlcyhmaWxsPUZBTFNFKSArCiAgICAgIHRpbHRfdGhlbWUKcDIyIDwtIGdncGxvdChkYXRhID0gdGFpbChuZWdfbCwgMjApLCBhZXMod29yZCwgRnJlcSwgZmlsbCA9IHdvcmQpKSArCiAgICAgIGdlb21fYmFyKHBvc2l0aW9uID0gImRvZGdlIiwgc3RhdCA9ICJpZGVudGl0eSIpICsKICAgICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IEZyZXEpLCB2anVzdCA9IDEuNiwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMywgYW5nbGU9OTApICsKICAgICAgZ2d0aXRsZSgiTmVnYXRpdmUgKExPVUdIUkFOKSIpICsKICAgICAgZ3VpZGVzKGZpbGw9RkFMU0UpICsKICAgICAgdGlsdF90aGVtZQptdWx0aXBsb3QocDE5LCBwMjAscDIxLCBwMjIsbGF5b3V0ID0gbWF0cml4KGMoMSwyLDMsNCksIG5yb3c9MiwgYnlyb3c9VFJVRSkpCgpgYGAKCkl0IGNhbiBiZSBzZWVuIHRoYXQgcG9zaXRpdmUgYW5kIG5lZ2F0aXZlIHdvcmRzIGFyZSBkaWZmZXJlbnQgZm9yIGFsbCB0aHJlZSBsZXhpY29ucy4gCgo=